#include "Calendar.h"
#include <stdio.h>
#include <assert.h>
#include <conio.h>
#include <algorithm>

/********************************************/
/*			Calendar						*/
/********************************************/

Calendar::Calendar(int nof_days){
	createDays(nof_days);
}

Calendar::~Calendar(){
	delete[] days;
}

CalendarError Calendar::createMeeting(const char *subject, float startTime, float endTime, int day, /*out*/const Meeting *&meeting){
	Day *dayp;
	CalendarError res = getDay(day, dayp);
	if (res != CALENDAR_ERROR_NONE) return res;

	meeting = new Meeting(startTime, endTime, subject, *dayp, *this); 
	if (meeting == NULL) return CALENDAR_ERROR_MEMORY;

	return CALENDAR_ERROR_NONE;

}

CalendarError Calendar::getDay(int day_num, /*out*/ Day *&day) {
	if (day_num <= 0 || day_num > nof_days) return CALENDAR_ERROR_PARAM;
	day = &(days[day_num-1]);
	return CALENDAR_ERROR_NONE;
}

CalendarError Calendar::createDays(int nof_days){
	this->nof_days = nof_days;
	this->days = new Day[7];
	for (int i=0; i<nof_days; i++){
		this->days[i] = Day(*this, i+1); // Sunday = 1 and so on...
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Calendar::getMeeting(const char *subject, float startTime, float endTime, int day, /*out*/const Meeting *&meeting){
	Day *dayP;
	CalendarError res = getDay(day, dayP);
	if (res != CALENDAR_ERROR_NONE) return res;

	res = dayP->findMeeting(subject, day, startTime, endTime, meeting); 
	if (res != CALENDAR_ERROR_NONE) return res;

	return CALENDAR_ERROR_NONE;
}

CalendarError Calendar::addMeeting(const Meeting &meeting){
	Day *mDay;
	CalendarError res = meeting.getDay(mDay);
	if (res!=CALENDAR_ERROR_NONE) return res;
	int i;
	res= mDay->getDayNumber(i);
	if (res!=CALENDAR_ERROR_NONE) return res;
	res = this->days[i-1].addMeeting(meeting);
	return res;
}

CalendarError Calendar::deleteMeeting(const Meeting &meeting){
	Day *mDay;
	CalendarError res = meeting.getDay(mDay);
	if (res!=CALENDAR_ERROR_NONE) return res;
	int i;
	res= mDay->getDayNumber(i);
	if (res!=CALENDAR_ERROR_NONE) return res;
	res = this->days[i-1].deleteMeeting(meeting);
	return res;
}

CalendarError Calendar::findMeetingStartingAt(float startTime, /*out*/ const Meeting **meetings, int maxNofMeetings, /*out*/ int &nofMeetings) const{
	CalendarError res;
	int innerCounter, innerMax;
	const Meeting **mResult;
	nofMeetings = 0;
	for (int i=0; i<this->nof_days; i++){
		mResult = meetings + nofMeetings;
		innerCounter = 0;
		innerMax = maxNofMeetings - nofMeetings;
		res = this->days[i].findMeetingStartingAt(startTime, mResult, innerMax, innerCounter);
		if (res!=CALENDAR_ERROR_NONE) return res;
		nofMeetings += innerCounter;
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Calendar::printAll() const{

	for (int i=0; i<this->nof_days; i++){
		this->days[i].printAll();
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Calendar::clean(){

	for (int i=0; i<this->nof_days; i++){
		this->days[i].clean();
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Calendar::moveMeeting(const Meeting &meeting, float startTime, float endTime, int day, /*out*/const Meeting *&newMeeting) {
	Day *oldDay;
	
	CalendarError res =  meeting.getDay(oldDay);
	if (res != CALENDAR_ERROR_NONE) return res;
	
	const char *subject;

	res = meeting.getSubject(subject);
	if (res != CALENDAR_ERROR_NONE) return res;

	res = createMeeting(subject, startTime, endTime, day, newMeeting);
	if (res != CALENDAR_ERROR_NONE) return res;
	
	res = oldDay->deleteMeeting(meeting);
	if (res != CALENDAR_ERROR_NONE) return res;
	delete &meeting;
	
	res = addMeeting(*newMeeting);
	if (res != CALENDAR_ERROR_NONE) return res;
	
	return CALENDAR_ERROR_NONE;
}
int Calendar::whoAmI() const {
	return CALENDAR;
}

/********************************************/
/*			Day								*/
/********************************************/

Day::Day(){ // Should have a default CTOR for creating an array of Days
	this->dayNumber = 0; // 0 means - Not Initialized
	meetingsList = list<const Meeting *> ();
}

Day::Day(Calendar &calendar, int dayNumber){
	this->calendar = &calendar;
	this->dayNumber = dayNumber;
	meetingsList = list<const Meeting *> ();
}
Day::Day(int dayNumber){
	this->dayNumber = dayNumber;
	meetingsList = list<const Meeting *> ();
}

Day::~Day(){
}

CalendarError Day::addMeeting(const Meeting &meeting){
	bool status;
	CalendarError res;
	Day *dayp;
	
	res = meeting.getDay(dayp);
	if (res != CALENDAR_ERROR_NONE) return res;
	if (dayp == NULL) return CALENDAR_ERROR_PARAM;

	float startTime, endTime;

	res = meeting.getStartTime(startTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	if (startTime < 0 || startTime >=24) return CALENDAR_ERROR_PARAM;

	
	res = meeting.getStartTime(endTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	if (endTime < 0 || endTime >=24) return CALENDAR_ERROR_PARAM;

	if (endTime < startTime) return CALENDAR_ERROR_PARAM;

	res = isOverlap(meeting, status);
	if (res != CALENDAR_ERROR_NONE) return res;
	if (status) return CALENDAR_ERROR_OVERLAP;
	meetingsList.push_back(&meeting);
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::deleteMeeting(const Meeting  &meeting){
	meetingsList.remove((Meeting * const) &meeting);
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::findMeetingStartingAt(float startTime, /*out*/const Meeting **meetings, int maxNofMeetings,/*out*/ int &howMany) const{
	howMany=0;
	float itStartTime;
	for (list<const Meeting *>::const_iterator it = meetingsList.begin(); it != meetingsList.end(); it++){
		if (maxNofMeetings <= howMany) return CALENDAR_ERROR_MEMORY;
		CalendarError res = (*it)->getStartTime(itStartTime);
		if (res != CALENDAR_ERROR_NONE) return res;
		if (itStartTime == startTime){
			if (meetings != NULL){
				meetings[howMany] = *it;
			}
			howMany++;
		}
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::findMeeting(const char *subject, int day, float startTime, float endTime,const  Meeting *&meeting) const{
	float itStartTime;
	float itEndTime;
	const char *itSubject;
	for (list<const Meeting *>::const_iterator it = meetingsList.begin(); it != meetingsList.end(); it++){
		CalendarError res = (*it)->getStartTime(itStartTime);
		if (res != CALENDAR_ERROR_NONE) return res;
		res = (*it)->getEndTime(itEndTime);
		if (res != CALENDAR_ERROR_NONE) return res;
		res = (*it)->getSubject(itSubject);
		if (res != CALENDAR_ERROR_NONE) return res;
		if (startTime == itStartTime && endTime == itEndTime && strcmp(itSubject, subject) == 0) {
			meeting = (*it);
			return CALENDAR_ERROR_NONE;
		}
	}
	return CALENDAR_ERROR_NOT_EXIST;
}

CalendarError Day::findMeetingsDuring(float startTime, float endTime, const Meeting **meetings,int maxNofMeetings, int &howMany) const{
	howMany=0;
	for (list<const Meeting *>::const_iterator it = meetingsList.begin(); it != meetingsList.end(); it++){
		if (maxNofMeetings <= howMany) return CALENDAR_ERROR_MEMORY;

		float itStartTime;
		float itEndTime;
		CalendarError res = (*it)->getStartTime(itStartTime);
		if (res != CALENDAR_ERROR_NONE) return res;
		res = (*it)->getEndTime(itEndTime);
		if (res != CALENDAR_ERROR_NONE) return res;
		
		if ( !( ((itStartTime <= startTime) && (itEndTime <= startTime)) || 
			   ((itStartTime >= endTime) && (itEndTime >= endTime))			))
		{
			if (meetings != NULL) meetings[howMany] = *it;
			howMany++;
		}
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::findMeetingBySubject(const char* subject, /*out*/ const Meeting **meetings, int maxSize,/*out*/ int &howMany) const{
	howMany=0;
	for (list<const Meeting *>::const_iterator it = meetingsList.begin(); it != meetingsList.end(); it++){
		if (howMany == maxSize) {
			return CALENDAR_ERROR_MEMORY;
		}

		const char *itSubject;
		CalendarError res = (*it)->getSubject(itSubject);
		if (res != CALENDAR_ERROR_NONE) return res;
		
		if (strcmp(itSubject,subject) == 0){
			if (meetings != NULL) meetings[howMany] = (*it);
			howMany++;
		}
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::isOverlap(const Meeting &meeting, /*out*/ bool &status) const{
	CalendarError res;
	status = false;

	for (list<const Meeting *>::const_iterator it = meetingsList.begin(); it != meetingsList.end(); it++){
		res = meeting.isOverlap(**it, status);
		if (res != CALENDAR_ERROR_NONE) return res;
		if (status) return CALENDAR_ERROR_NONE;
	}
	
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::getDayNumber(int &dayOfWeek) const{
	dayOfWeek = this->dayNumber;
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::printAll()  const{

	for (list<const Meeting *>::const_iterator it = meetingsList.begin(); it != meetingsList.end(); it++){
		(*it)->printAll();
	}
	return CALENDAR_ERROR_NONE;
}

CalendarError Day::clean() {
	meetingsList.clear();
	return CALENDAR_ERROR_NONE;
}

void Day::operator= (const Day& day){
	if (this != &day){
		calendar = day.calendar;
		this->dayNumber = day.dayNumber;
		this->meetingsList = day.meetingsList;
	}
}

bool Day::operator== (const Day& day) const{
	if (this != &day){
		return ( calendar == day.calendar		    &&
				 dayNumber == day.dayNumber			&&
				 this->meetingsList == day.meetingsList);
	}
	return false;
}

CalendarError Day::getCalendar(/*out*/ Calendar *&calendar) const{
	calendar = this->calendar;
	return CALENDAR_ERROR_NONE;
}

int Day::whoAmI() const {
	return DAY;
}

/********************************************/
/*			Meeting							*/
/********************************************/

Meeting::Meeting() { // Should have a default CTOR for creating an array of Meetings
};

Meeting::Meeting(float startTime, float endTime, const char* subject) {
	setStartTime(startTime);
	setEndTime(endTime);
	setSubject(subject);
};

Meeting::Meeting(float startTime, float endTime, const char* subject, Day &day, Calendar &calendar) {
	this->calendar = &calendar;
	this->day = &day;
	setStartTime(startTime);
	setEndTime(endTime);
	setSubject(subject);
};

Meeting::~Meeting() {
};

CalendarError Meeting::getStartTime(float &startTime) const{
	startTime = this->startTime;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::getEndTime(float &endTime) const{
	endTime = this->endTime;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::getSubject(const char *&subject) const {
	subject = (char*) this->subject; 
	return CALENDAR_ERROR_NONE;
}

 CalendarError Meeting::getDay(Day *&day) const{
	day = this->day;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::printAll() const{
	float startTime;
	CalendarError res = getStartTime(startTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	int startTimeHour = (int) startTime;
	int startTimeMinutes = 60*(startTime - (int) startTime);

	float endTime;
	res = getEndTime(endTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	int endTimeHour = (int) endTime;
	int endTimeMinutes = 60*(endTime - (int) endTime);

	Day *thisDay;
	res = this->getDay(thisDay);
	if (res != CALENDAR_ERROR_NONE) return res;

	int dayNumber;
	res = thisDay->getDayNumber(dayNumber);
	if (res != CALENDAR_ERROR_NONE) return res;

	cout << "+------------------------------+-----+----------+--------+"<< endl;
	cout << "|            Subject           | Day | Start at | End at |"<< endl;
	cout << "+------------------------------+-----+----------+--------+"<< endl;
	printf( "| %-29s| %3d |   %02d:%02d  |  %02d:%02d |\n", this->subject, dayNumber, startTimeHour, startTimeMinutes, endTimeHour, endTimeMinutes);
	cout << "+------------------------------+-----+----------+--------+"<< endl;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::setStartTime(float startTime) {
	this->startTime = startTime;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::setEndTime(float endTime) {
	this->endTime = endTime;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::setSubject(const char *subject)  {
	this->subject = subject;
	return CALENDAR_ERROR_NONE;
}

CalendarError Meeting::setDay(Day &day){
	this->day = &day;
	return CALENDAR_ERROR_NONE;
}

void Meeting::operator= (const Meeting& meeting){
	if (this != &meeting){
		calendar = meeting.calendar;
		day = meeting.day;
		startTime = meeting.startTime;
		endTime = meeting.endTime;
		subject = meeting.subject;
	}
}

bool Meeting::operator== (const Meeting& meeting) const{
	if (this != &meeting){
		return ( calendar == meeting.calendar	    &&
				 day == meeting.day					&&
				 startTime == meeting.startTime		&&
				 endTime == meeting.endTime			&&
				 subject == meeting.subject);
	}
	return false;
}

CalendarError Meeting::isOverlap(const Meeting &meeting, /*out*/ bool &status) const{
	Day *day, *itday;
	float itStartTime, itEndTime, startTime, endTime;

	
	CalendarError res = getStartTime(startTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	res = getEndTime(endTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	res = meeting.getDay(day);
	if (res != CALENDAR_ERROR_NONE) return res;

	res = meeting.getDay(itday);
	if (res != CALENDAR_ERROR_NONE) return res;
	res = meeting.getStartTime(itStartTime);
	if (res != CALENDAR_ERROR_NONE) return res;
	res = meeting.getEndTime(itEndTime);
	if (res != CALENDAR_ERROR_NONE) return res;

	if (itday ==day) {
		if ( !( ((itStartTime <= startTime) && (itEndTime <= startTime)) || 
				((itStartTime >= endTime) && (itEndTime >= endTime))			))
		{
			status = true;
			return CALENDAR_ERROR_NONE;
		}
	}
	return CALENDAR_ERROR_NONE;
}

int Meeting::whoAmI() const {
	return MEETING;
}